iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0
Mobile Development

Android 性能戰爭:從 Profiler 開始的 30 天優化實錄系列 第 18

# Day 18:【流暢度戰爭】佈局優化的勝利:ConstraintLayout vs LinearLayout

  • 分享至 

  • xImage
  •  

各位戰士,歡迎來到第十八天的戰場。在 UI 流暢度的陣地戰中,最常見的敵人之一就是「過於複雜的佈局」。一個層層堆疊、深度過深的佈局,就像一個臃腫的官僚體系,會讓 Android 系統在渲染的「測量 (Measure)」與「佈局 (Layout)」階段執行大量重複且低效的工作。

RecyclerView 這樣的場景中,這種低效會被成百上千倍地放大,每一次滾動都可能引發卡頓。今天的任務,就是學習如何精簡我們的佈局結構,打造一個扁平化、高效率的指揮體系


頭號敵人:巢狀佈局與「雙重稅負」

在過去,LinearLayoutRelativeLayout 是我們佈局的主力。但為了實現稍複雜的 UI,我們常常會陷入「佈局巢狀 (Nesting)」的陷阱。

想像一個常見的卡片佈局:

<LinearLayout
    android:orientation="vertical" ...>

    <TextView ... />

    <LinearLayout
        android:orientation="horizontal" ...>
        
        <ImageView android:id="@+id/avatar" ... />

        <TextView android:id="@+id/name" ... />
        
    </LinearLayout

</LinearLayout>

這樣的結構會產生一個深度為 3 的視圖層級。更糟糕的是,如果你在 LinearLayout 中使用了 layout_weight 屬性,系統為了計算剩餘空間該如何分配,往往需要對其子 View 進行兩次測量。這就是臭名昭著的「雙重稅負 (Double Taxation)」問題,它會讓測量階段的耗時加倍。

主力武器:ConstraintLayout 的降維打擊
為了解決巢狀佈局的頑疾,Google 推出了 ConstraintLayout(約束佈局)。它的核心思想截然不同:

ConstraintLayout 讓你透過建立「約束關係」,來描述佈局中各個元件 (View) 之間的位置關係,從而實現幾乎完全扁平化的佈局層級。

讓我們用 ConstraintLayout 來重構上面的例子:
ConstraintLayout 讓你透過建立「約束關係」,來描述佈局中各個元件 (View) 之間的位置關係,從而實現幾乎完全扁平化的佈局層級。

讓我們用 ConstraintLayout 來重構上面的例子:

<androidx.constraintlayout.widget.ConstraintLayout ...>

    <TextView
        android:id="@+id/title"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent" ... />

    <ImageView
        android:id="@+id/avatar"
        app:layout_constraintTop_toBottomOf="@id/title"
        app:layout_constraintStart_toStartOf="parent" ... />

    <TextView
        android:id="@+id/name"
        app:layout_constraintStart_toEndOf="@id/avatar"
        app:layout_constraintTop_toTopOf="@id/avatar"
        app:layout_constraintBottom_toBottomOf="@id/avatar" ... />

</androidx.constraintlayout.widget.ConstraintLayout>

戰果分析:

  • 層級扁平化:我們用一層 ConstraintLayout 就完成了之前三層 LinearLayout 才能做到的事。視圖樹的深度從 3 降到了 2。

  • 性能提升:測量和佈局的計算量大幅減少,沒有了「雙重稅負」的風險。

  • 靈活性:ConstraintLayout 還提供了 Guideline, Barrier, Chain 等強大功能,可以實現遠比傳統佈局更複雜的 UI,同時保持扁平的層級。

在所有可以拍扁層級的場景,ConstraintLayout 都應該是你的首選武器。

特種部隊:<merge> 標籤的奇襲
有時候,我們為了重用性,會把一部分 UI 抽成一個獨立的 XML 檔案,然後用 <include> 標籤引入。這時,一個新的敵人——「冗餘的父佈局」——就可能出現。

場景:假設你有一個主佈局是 LinearLayout,而你抽出來的 custom_title.xml 的根佈局也是一個 LinearLayout

<LinearLayout android:orientation="vertical" ...>
    <include layout="@layout/custom_title" />
    </LinearLayout>

<LinearLayout android:orientation="horizontal" ...>
    <ImageView ... />
    <TextView ... />
</LinearLayout>

include 發生時,視圖結構會變成 LinearLayout > LinearLayout > (ImageView, TextView),產生了一個完全多餘的佈局層級。

解決方案:使用 <merge> 標籤。

<merge> 是一個神奇的標籤,它本身不會被繪製到視圖層級中,而是會將它的子 View 直接合併到它被 include 進去的父佈局裡。

<merge xmlns:android="[http://schemas.android.com/apk/res/android](http://schemas.android.com/apk/res/android)">
    <ImageView ... />
    <TextView ... />
</merge>

使用 <merge> 後,視圖結構變成了 LinearLayout > (ImageView, TextView),我們兵不血刃地消滅了一個冗餘的佈局層級。

驗證勝利:請出 Layout Inspector

我們如何確認佈局真的被優化了?

Android Studio 內建的 Layout Inspector (工具列 Tools > Layout Inspector) 是我們的驗收工具。它可以視覺化地展示 App 當前畫面的視圖樹結構。你可以對比優化前後的視圖樹,清晰地看到層級是否減少。

當然,最終的勝利證明,還是在 Perfetto 的 Trace 報告中看到那個曾經耗時的 onMeasure/onLayout 區塊,變得又短又快。

今日總結

今天,我們學習了佈局優化戰役中的核心戰術:盡一切可能壓平視圖層級。

  • 我們認識到巢狀佈局,特別是帶有 layout_weightLinearLayout,是性能的殺手。
  • 我們掌握了主力武器 ConstraintLayout,用它來構建複雜但扁平的 UI。
  • 我們學會了使用特種兵 <merge> 標籤,在 <include> 場景中消除冗餘的父佈局。

佈局的戰場已經清理乾淨。但 UI 流暢度的戰爭遠未結束。明天,我們將把目光投向另一個、也是最常見的 Jank 發生地——RecyclerView。我們將學習一些超越 ViewHolder 模式的進階優化技巧。

我們明天見!


上一篇
# Day 17:【流暢度戰爭】 systrace/perfetto:終極卡頓分析神器
下一篇
# Day 19:【流暢度戰爭】RecyclerView 優化:不只是 ViewHolder
系列文
Android 性能戰爭:從 Profiler 開始的 30 天優化實錄24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言